home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
301-325
/
disk_325
/
fam
/
fam.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-06
|
34KB
|
1,382 lines
/*
* FAM.c -- Rexx File Access Manager and Directory Buffer.
* Copyright 1990 Darren New. All Rights Reserved.
* Originally for DIBBS -- Darren's Innovative Bulletin Board Server
*/
/******************************************************************
This program allows multiple ARexx programs to access a buffered version of
a directory in a consistent and serialized manner. This program and all
derivative works are copyright 1990 Darren New. All Rights Reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided
that the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation, and that the names of the copyright holder or author not be
used in advertising or publicity pertaining to disstribution of the
software without specific, written prior permission.
BOTH THE AUTHOR AND THE COPYRIGHT HOLDER DISCLAIM ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDER OR THE
AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
VERSION 1.1 Feb 1990
***********************************************************/
#define PROGNAME "FAM"
#define PROGVERS "1.1"
#define ERRLEVEL 10L /* error level to return */
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "ctype.h"
#include "minrexx.h"
#ifdef LMS
#include "low-mem.h" /* ASDG's low memory server */
#endif
#ifdef CBACK
#define assert(exp)
#else
#include "assert.h"
#endif
#include "exec/memory.h"
#include "exec/lists.h"
#include "libraries/dos.h"
#include "proto/exec.h"
#include "proto/dos.h"
/* Because remove and insert are used very heavily during rescan, these
* have been turned into macros to let the optimizer bash at them.
* The InsertQ only works when the node to be inserted already points
* to the correct nodes to insert between.
*/
#define Remove(node) ((node)->ln_Pred->ln_Succ=(node)->ln_Succ,(node)->ln_Succ->ln_Pred=(node)->ln_Pred)
#define InsertQ(node) ((node)->ln_Pred->ln_Succ=(node),(node)->ln_Succ->ln_Pred=(node))
/*
* Here is our command association list. Note that in this case,
* we are setting the userdata field to be a function to call.
* Dispatch will still take place through disp(), so common head
* and tail stuff can go there.
*
* Commands are all lower case, so we match either upper or lower.
* (This is a requirement of minrexx.)
*/
void FAMversion (struct RexxMsg *msg, char *p);
void FAMopen (struct RexxMsg *msg, char *p);
void FAMclose (struct RexxMsg *msg, char *p);
void FAMclear (struct RexxMsg *msg, char *p);
void FAMexpunge (struct RexxMsg *msg, char *p);
void FAMgetioerr (struct RexxMsg *msg, char *p);
void FAMgetdirname (struct RexxMsg *msg, char *p);
void FAMgetbinextens (struct RexxMsg *msg, char *p);
void FAMgetbufendline (struct RexxMsg *msg, char *p);
void FAMgetnamecount (struct RexxMsg *msg, char *p);
void FAMgetnames (struct RexxMsg *msg, char *p);
void FAMgetinfo (struct RexxMsg *msg, char *p);
void FAMrescan1 (struct RexxMsg *msg, char *p);
void FAMrescan (struct RexxMsg *msg, char *p);
void FAMgrep (struct RexxMsg *msg, char *p);
void FAMnewfile (struct RexxMsg *msg, char *p);
void FAMlockfile (struct RexxMsg *msg, char *p);
struct rexxCommandList rcl[] = {
{ "version", (APTR)&FAMversion },
{ "open", (APTR)&FAMopen },
{ "close", (APTR)&FAMclose },
{ "clear", (APTR)&FAMclear },
{ "expunge", (APTR)&FAMexpunge },
{ "getioerr", (APTR)&FAMgetioerr },
{ "getdirname", (APTR)&FAMgetdirname },
{ "getbinextens", (APTR)&FAMgetbinextens },
{ "getbufendline", (APTR)&FAMgetbufendline },
{ "getnamecount", (APTR)&FAMgetnamecount },
{ "getnames", (APTR)&FAMgetnames },
{ "getinfo", (APTR)&FAMgetinfo },
{ "rescan1", (APTR)&FAMrescan1 },
{ "rescan", (APTR)&FAMrescan },
{ "grep", (APTR)&FAMgrep },
{ "newfile", (APTR)&FAMnewfile },
{ "lockfile", (APTR)&FAMlockfile },
{ NULL, NULL } } ;
/********************************************************************
* GLOBAL STATUS VARIABLES
********************************************************************/
/*
* Here are the globals used by Lattice's CBack.o
*
*/
#ifdef CBACK
LONG _stack = 4000; /* stack space */
char *_procname = PROGNAME "-task";/* process name */
LONG _priority = 0; /* default priority */
LONG _BackGroundIO = 1; /* I want to output some messages */
extern LONG _Backstdout; /* FileHandle to output to */
#endif
/*
* Here are the globals for keeping track of our command-line arguments
* and our current status.
*
*/
char VersionString[] = PROGNAME " V" PROGVERS " COMPILED " __DATE__ " " __TIME__;
char * PortName; /* name of ARexx port to open */
char * DirPath; /* full path to directory */
char * GrepFuncName; /* full path to GREP to LoadSeg() */
char * BinExtens; /* file extensions of "binary" files */
char * BufEndLine; /* line to end buffering */
int OpenCount; /* number of outstanding OPENs */
int ExpungeWanted; /* have received the EXPUNGE command */
int ClearWanted; /* have received the CLEAR command */
BPTR GrepEntry; /* GREP function entry point */
#ifdef LMS
struct Library * LowMemBase;/* ASDG Low Memory Base address */
struct LowMemMsg LMSSpace; /* ASDG low memory notifier */
int LowMemIsOpen; /* true if we are handling LowMem stuff */
#endif
/*
* Here are the globals for holding the directory information
*
*/
#include "FAM.h"
struct List ScanList; /* the actual ScanList */
struct List TempScanList; /* an alternate copy (for sorting) */
/* returns true if list is empty */
#define IsEmpty(head) ((long) ((head).lh_TailPred) == (long) (&(head)))
/* returns pointer to first node on list */
#define FirstNode(head) ((struct ScanListNode *)((head)->lh_Head->ln_Succ))
/* returns pointer to next node on list */
#define NextNode(n) ((struct ScanListNode *)((n)->node.ln_Succ))
/* returns true if node is valid ScanListNode */
#define NotLastNode(n) ((n)&&(n)->node.ln_Succ)
/* returns true if a < b */
#define StrLT(a, b) (strcmp((a), (b)) < 0)
/*
* These globals are passed around during parsing.
*
*/
long CMDargs[9]; /* what numeric args did we see to this function? */
int CMDparsed; /* 0 if argument parsing successful, !0 for errnum */
int CMDuserreplied; /* has the current message been replied to yet? */
/*
* mallocFAM(), freeFAM() and strdupFAM() have the same interface as
* malloc(), free(), and strdup() except that they actually return the
* memory back to the system once it is freed.
*/
char *strdupFAM(char * s);
void *mallocFAM(unsigned size);
void freeFAM(void * p);
char *strdupFAM(char * s)
{
char * p = mallocFAM(strlen(s)+1);
if (p) strcpy(p, s);
return p;
}
void * mallocFAM(unsigned size)
{
long * p = (long *) AllocMem(size+4, MEMF_CLEAR);
if (p) {
*p++ = (long) size;
}
return p;
}
void freeFAM(void * ptr)
{
long * p = ptr;
long size = *--p;
FreeMem(p, size+4);
}
/*
* This is our dispatch function. We call our handler function.
* If our handler replied, we return a 1 to indicate that.
* If the parse and everything else was successful, we return a 0.
* Otherwise, we return a failure of 10 to indicate that the arguments
* were messed up.
*/
int disp(struct RexxMsg *msg, struct rexxCommandList *dat, char *p);
int disp(struct RexxMsg *msg, struct rexxCommandList *dat, char *p)
{
CMDparsed = 0 ;
CMDuserreplied = 0 ;
((int (*)(struct RexxMsg *, char *))(dat->userdata))(msg, p) ;
if (CMDparsed == 0) {
if (!CMDuserreplied)
replyRexxCmd(msg, 0L, 0L, NULL) ;
CMDuserreplied = 1;
}
else {
replyRexxCmd(msg, ERRLEVEL, (long) CMDparsed, NULL) ;
CMDuserreplied = 1;
}
return CMDuserreplied;
}
void errout(char * s, int exiting);
void errout(char * s, int exiting)
{
#ifdef CBACK
if (_Backstdout == NULL) {
_Backstdout = Open("CON:0/0/400/100/RSD output", MODE_NEWFILE);
}
if (_Backstdout)
Write(_Backstdout, s, (long)strlen(s));
#else
fprintf(stderr, "%s", s);
#endif
#ifdef CBACK
if (exiting && _Backstdout)
Close(_Backstdout);
if (exiting) Delay(500);
#endif
if (exiting) {
fclose(stdin);
fclose(stdout);
/* #ifdef CBACK */
fclose(stderr);
/* #endif */
}
}
#ifdef LMS
/*
* This handles the Low Memory message coming from the ASDG Low Memory Server.
*
*/
int HandleLMS(struct Message * msg);
int HandleLMS(struct Message * msg)
{
if (msg != (struct Message *) &LMSSpace) /* EH? */
return 0;
if (LMSSpace.lm_flag != LM_LOW_MEMORY_CONDITION) /* EH?? */
return 0;
LMSSpace.lm_flag = LM_CONDITION_ACKNOWLEDGED; /* ASDG foolishness */
FAMclear(NULL, "");
return 0;
}
#endif
/*
* Our main routine
*
*/
void main(int argc, char * * argv);
void main(int argc, char * * argv)
{
long rexxbit; /* this is the Signal bit to wait for Rexx on */
BPTR olddir, newdir;
void FreeScanList(struct List * list);
if (argc == 0) {
errout("Can't run from WorkBench yet!\n", 1);
exit(20); /* can't run from workbench yet */
}
errout(VersionString, 0);
errout("\n", 0);
if (argc < 4 || 6 < argc) {
errout("Usage: " PROGNAME " portname dirpath grepfunc [binextens [bufendline]]\n", 1);
exit(20);
}
PortName = argv[1];
DirPath = argv[2];
GrepFuncName = argv[3];
if (5 <= argc)
BinExtens = argv[4];
if (6 <= argc)
BufEndLine = argv[5];
GrepEntry = LoadSeg(GrepFuncName);
if (GrepEntry == 0) {
char buf[100];
sprintf(buf, "Could not LoadSeg GREP function \"%s\"!\n", GrepFuncName);
errout(buf, 1);
exit(20);
}
newdir = Lock(DirPath, ACCESS_READ);
if (newdir) {
olddir = CurrentDir(newdir);
/* UnLock(olddir); */ /* CBack seems to munch this */
}
else {
char buf[100];
sprintf(buf, "Could not change to directory \"%s\"!\n", DirPath);
errout(buf, 1);
UnLoadSeg(GrepEntry);
exit(20);
}
rexxbit = upRexxPort(PortName, rcl, NULL, &disp) ;
if (rexxbit == 0) {
char buf[100];
sprintf(buf, "Could not open ARexx port \"%s\"!\n", PortName);
errout(buf, 1);
UnLoadSeg(GrepEntry);
exit(20);
}
#ifdef LMS
/* Attempt to use ASDG low memory server; don't gripe if not there */
LowMemBase = OpenLibrary(LMSName, 0L);
if (LowMemBase != NULL) {
if (0 == RegLowMemReq(PortName , &LMSSpace)) {
HandleNonRexx = HandleLMS;
LMSSpace.lm_flag = LM_CONDITION_ACKNOWLEDGED;
LowMemIsOpen = 1;
}
else {
CloseLibrary(LowMemBase);
LowMemBase = NULL;
}
}
#endif
{ /* all went well */
char buf[100];
sprintf(buf, "ARexx port \"%s\" now available.\n", PortName);
errout(buf, 1);
}
/* Here, initialize the ScanList */
NewList(&ScanList); NewList(&TempScanList);
ScanList.lh_Type = NT_FILELIST;
TempScanList.lh_Type = NT_FILELIST;
do {
Wait(rexxbit);
dispRexxPort();
} while (! (OpenCount == 0 && ExpungeWanted));
if (GrepEntry)
UnLoadSeg(GrepEntry);
GrepEntry = NULL;
#ifdef LMS
if (LowMemIsOpen) {
LMSSpace.lm_flag = 0; /* cancel any incomming messages */
DeRegLowMemReq(PortName);
}
if (LowMemBase) {
CloseLibrary(LowMemBase);
LowMemBase = NULL;
}
#endif
CurrentDir(olddir); /* CBack doesn't like changing dirs */
UnLock(newdir);
FreeScanList(&ScanList);
FreeScanList(&TempScanList);
dnRexxPort();
exit(0);
}
/*****************************************************************
* Parsing routines
*****************************************************************/
/*
* This function takes a pointer to a pointer to a string, grabs the
* next number, returns it, and advances the pointer to the string to
* point after the number.
*/
long getnm(char ** where);
long getnm(char ** where)
{
char *p = *where;
long val = 0;
long gotone = 0;
while (*p <= ' ' && *p)
p++;
if (!*p)
CMDparsed = 17; /* not enuf args */
while ('0' <= *p && *p <= '9') {
gotone = 1;
val = 10 * val + *p++ - '0';
}
if (gotone == 0 && CMDparsed == 0)
CMDparsed = 47; /* invalid args; not a number */
*where = p;
return val;
}
/*
* This function trys to find `n' numeric arguments in the command
* string, and stuffs them into the CMDargs array.
*/
void parseargs(char * p, long n);
void parseargs(char * p, long n)
{
int i ;
while (*p > ' ' && *p)
p++ ;
for (i=0; i<n && CMDparsed == 0; i++)
CMDargs[i] = getnm(&p) ;
}
/*******************************************************************
* List manipulation and directory scanning routines
*******************************************************************/
/*
* This function takes a ScanListNode and deallocates it.
* The node MUST be Remove()d before calling this.
*/
void FreeNode(struct ScanListNode * node);
void FreeNode(struct ScanListNode * node)
{
assert(node);
assert(node->node.ln_Type == NT_FILENAME);
if (node->node.ln_Name) freeFAM(node->node.ln_Name);
if (node->filenote) freeFAM(node->filenote);
if (node->contents) freeFAM(node->contents);
freeFAM(node);
}
/*
* This function allocates a new node, clearing out most of the fields.
*/
struct ScanListNode * AllocNode(void);
struct ScanListNode * AllocNode(void)
{
struct ScanListNode * node;
node = mallocFAM(sizeof(struct ScanListNode));
/* mallocFAM MUST clear the memory it gets */
if (node != NULL)
node->node.ln_Type = NT_FILENAME;
return node;
}
/*
* This function takes a file info block and a ScanListNode and
* copies the information from the fib to the node. Only items
* that have changed actually get reallocated. If NULL is passed in for
* node, then node will be allocated. If NULL is returned, then
* some memory allocation failed and THE NODE HAS BEEN FREED!
* Hence, this node must NOT be in a list when FillInNode is called!
*/
struct ScanListNode * FillInNode(struct FileInfoBlock * fib, struct ScanListNode * node);
struct ScanListNode * FillInNode(struct FileInfoBlock * fib, struct ScanListNode * node)
{
long newdate, newtime;
if (node == NULL) node = AllocNode();
if (node == NULL) return NULL;
assert(node->node.ln_Type == NT_FILENAME);
if (node->node.ln_Name == NULL ||
0 != strcmp(node->node.ln_Name, fib->fib_FileName)) {
if (node->node.ln_Name != NULL)
freeFAM(node->node.ln_Name);
node->node.ln_Name = strdupFAM(fib->fib_FileName);
if (node->node.ln_Name == NULL) {
FreeNode(node);
return NULL;
}
}
node->protection = fib->fib_Protection;
node->isdir = 0 < fib->fib_DirEntryType;
newdate = fib->fib_Date.ds_Days;
newtime = (fib->fib_Date.ds_Minute * 60L) +
(fib->fib_Date.ds_Tick / 50L);
if (newtime != node->filetime || newdate != node->filedate) {
/* assume line count didn't change if date didn't change */
node->sizelines = -1L;
}
node->filedate = newdate;
node->filetime = newtime;
node->sizebytes = fib->fib_Size;
node->isNewFile = node->isDeleted = node->isGrepped = 0;
if (node->filenote == NULL ||
0 != strcmp(node->filenote, fib->fib_Comment)) {
if (node->filenote != NULL) {
freeFAM(node->filenote);
node->filenote = NULL;
}
if (0 < strlen(fib->fib_Comment))
node->filenote = strdupFAM(fib->fib_Comment);
if (node->node.ln_Name == NULL) {
FreeNode(node);
return NULL;
}
}
if (node->sizelines == -1L && BinExtens) {
register unsigned long i;
char * fext;
/* First, check to see if it has an extension */
fext = strchr(node->node.ln_Name, '.');
if (fext && *fext) {
/* we found an extension */
/* brutally inefficient... */
for (i = 0; i < strlen(BinExtens); i++) {
if ( 0 == strncmp(fext, &BinExtens[i], strlen(fext)) &&
(BinExtens[i+strlen(fext)] == '.' ||
BinExtens[i+strlen(fext)] == '\0') ) {
node->sizelines = -2L; /* It's a binary all right */
break;
}
}
}
}
if (node->sizelines == -1L && BinExtens) {
char * buf;
register char * pnt;
register long len;
register unsigned long i;
long count;
BPTR handle = Open(node->node.ln_Name, MODE_OLDFILE);
if (handle == NULL) return node; /* could not count */
buf = mallocFAM(10 + node->sizebytes);
if (buf == NULL) {
Close(handle);
return node;
}
len = Read(handle, buf, node->sizebytes + 5);
if (len != node->sizebytes) {
freeFAM(buf);
Close(handle);
return node;
}
pnt = buf; count = 0;
while (pnt = strchr(pnt, '\n')) {count += 1; pnt += 1;}
node->sizelines = count;
if (node->contents) {
freeFAM(node->contents);
node->contents = NULL;
}
if (BufEndLine) {
/* first, toss old contents */
if (node->contents) {
freeFAM(node->contents);
node->contents = NULL;
}
/* see if we can find BufEndLine in file */
pnt = buf; len = strlen(BufEndLine);
while (pnt && 0 != strncmp(BufEndLine, pnt, len)) {
pnt = strchr(pnt, '\n');
if (pnt) pnt++;
}
if (pnt) {
/* we found BufEndLine -- move to contents */
i = pnt - buf;
node->contents = mallocFAM(i + 2);
if (node->contents) {
strncpy(node->contents, buf, i);
node->contents[i] = '\0';
}
}
}
freeFAM(buf); Close(handle);
}
return node;
}
/*
* This function empties the entire list given to it.
*/
void FreeScanList(struct List * list);
void FreeScanList(struct List * list)
{
struct ScanListNode * node;
while (!(IsEmpty(*list))) {
node = (struct ScanListNode *) RemHead(list);
FreeNode(node);
}
}
/* This is a secret communication path for optimization of RESCAN */
static struct ScanListNode * InsAlphaPrev;
/*
* This function takes a ScanList and a string and finds the node
* in the list that the string would follow if it were the name
* of a node to be inserted alphabetically. Assumes the list is already
* alphabetical.
*/
struct ScanListNode * FindPrev(struct List * list, struct ScanListNode * node);
struct ScanListNode * FindPrev(struct List * list, struct ScanListNode * node)
{
struct ScanListNode * index;
char * name = node->node.ln_Name;
if (IsEmpty(*list)) {
return (struct ScanListNode *) list;
}
if (InsAlphaPrev && StrLT(InsAlphaPrev->node.ln_Name, name))
index = InsAlphaPrev;
else
index = FirstNode(list);
for ( ; NotLastNode(index); index = NextNode(index)) {
if (StrLT(name, index->node.ln_Name)) {
return (struct ScanListNode *) (index->node.ln_Pred);
}
}
return (struct ScanListNode *) (&(list->lh_Tail));
}
/*
* This function takes a node and a list and inserts the node in
* the correct alphabetical place in the list. The node should not be part
* of another list at the time.
*/
void InsAlpha(struct List * list, struct ScanListNode * node);
void InsAlpha(struct List * list, struct ScanListNode * node)
{
struct ScanListNode * prev;
if (InsAlphaPrev == node) InsAlphaPrev = NULL;
prev = FindPrev(list, node);
Insert(list, (struct Node *) node, (struct Node *) prev);
}
/*******************************************************************
* Here we have the actual command-handling code.
*******************************************************************/
/*
* This handler returns the version of the program.
*/
void FAMversion(struct RexxMsg *msg, char *p)
{
CMDuserreplied = 1 ;
replyRexxCmd(msg, 0L, 0L, VersionString);
}
/*
* This handler opens the device or file.
*/
void FAMopen(struct RexxMsg *msg, char *p)
{
char buf[20];
if (IsEmpty(ScanList)) {
FAMrescan(msg, "");
}
if (IsEmpty(ScanList)) {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
}
else {
OpenCount += 1;
sprintf(buf, "%d", OpenCount);
replyRexxCmd(msg, 0L, 0L, buf);
CMDuserreplied = 1;
}
}
/*
* This handler closes the device or file.
*/
void FAMclose(struct RexxMsg *msg, char *p)
{
char buf[20];
while (*p <= ' ' && *p) p++;
if (OpenCount == 0) {
return;
}
if (0 == strcmp(p, "NOW") && 1 < OpenCount) {
OpenCount = 1;
}
OpenCount -= 1;
if (OpenCount == 0 && ClearWanted) {
FreeScanList(&ScanList);
FreeScanList(&TempScanList);
ClearWanted = 0;
}
sprintf(buf, "%d", OpenCount);
if (msg) {
replyRexxCmd(msg, 0L, 0L, buf);
CMDuserreplied = 1;
}
}
/*
* This handler sets the clear flag and possibly clears the ScanList.
* It does not reply if msg is NULL, as we might call it from the
* ASDG low memory handler.
*/
void FAMclear(struct RexxMsg *msg, char *p)
{
char buf[20];
if (IsEmpty(ScanList)) {
return;
}
if (OpenCount == 0) {
FreeScanList(&ScanList);
FreeScanList(&TempScanList);
}
else {
ClearWanted = 1;
}
if (msg) {
sprintf(buf, "%d", OpenCount);
replyRexxCmd(msg, 0L, 0L, buf);
CMDuserreplied = 1;
}
}
/*
* This handler expunges the program.
*/
void FAMexpunge(struct RexxMsg *msg, char *p)
{
ExpungeWanted = 1;
ClearWanted = 1;
while (*p <= ' ' && *p) p++;
if (0 == strcmp(p, "NOW")) {
if (OpenCount) FAMclose(msg, p);
}
}
/*
* This handler returns the most recent IO error number.
*/
void FAMgetioerr(struct RexxMsg *msg, char *p)
{
long ans;
char buf[20];
ans = IoErr();
sprintf(buf, "%ld", ans);
replyRexxCmd(msg, 0L, 0L, buf); /* generally errored */
CMDuserreplied = 1;
}
/*
* This handler returns the directory path.
*/
void FAMgetdirname(struct RexxMsg *msg, char *p)
{
CMDuserreplied = 1 ;
replyRexxCmd(msg, 0L, 0L, DirPath);
}
/*
* This handler returns the binary extensions.
*/
void FAMgetbinextens(struct RexxMsg *msg, char *p)
{
CMDuserreplied = 1 ;
if (BinExtens)
replyRexxCmd(msg, 0L, 0L, BinExtens);
else
replyRexxCmd(msg, 1L, 0L, NULL);
}
/*
* This handler returns the buffer end line.
*/
void FAMgetbufendline(struct RexxMsg *msg, char *p)
{
CMDuserreplied = 1 ;
if (BufEndLine)
replyRexxCmd(msg, 0L, 0L, BufEndLine);
else
replyRexxCmd(msg, 1L, 0L, NULL);
}
/*
* This handler returns the current count of names.
*/
void FAMgetnamecount(struct RexxMsg *msg, char *p)
{
struct ScanListNode * node;
long count;
char buf[20];
if (IsEmpty(ScanList)) {
count = 0;
}
else {
count = 1;
for (node = FirstNode(&ScanList); NotLastNode(node);
node = NextNode(node)) {
count += 1;
}
}
sprintf(buf, "%ld", count);
replyRexxCmd(msg, 0L, 0L, buf);
CMDuserreplied = 1;
}
/*
* This handler returns the current list of names.
*/
void FAMgetnames(struct RexxMsg *msg, char *p)
{
char * __stdargs ListNames(struct List *, char); /* in RexxGlue */
void __stdargs DeleteArgstring(char * arg); /* in RexxGlue */
char * result;
char pad = ' ';
while (*p <= ' ' && *p) p++;
if (' ' < *p && *p <= 0x7F) pad = *p;
if (0 == openRexxLib()) {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
}
result = ListNames(&ScanList, pad);
if (result) {
replyRexxCmd(msg, 0L, 0L, result);
CMDuserreplied = 1;
openRexxLib(); /* because replyRexxCmd closes it! */
DeleteArgstring(result);
}
else {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
}
closeRexxLib();
}
/*
* This handler returns the information for one file.
*/
void FAMgetinfo(struct RexxMsg *msg, char *p)
{
static char protchars[33] = "DEWRAPSH"; /* allow room for patching */
char buf[350];
unsigned short len, i;
char c;
struct ScanListNode * node;
char * argbuf;
while (*p <= ' ' && *p) p++;
if (!*p) {
CMDparsed = 17;
return;
}
node = (struct ScanListNode *) FindName(&ScanList, p);
if (node == NULL) {
CMDparsed = 18;
return;
}
strcpy(buf, node->isdir ? "DIR -" : "FILE -");
/* '-' starts protection bits */
for (len = strlen(buf), i = 0; i < strlen(protchars); i++) {
if (0 == (node->protection & (1 << i))) {
buf[len++] = protchars[i];
}
}
c = '-';
if (node->isLocked) c = 'L';
if (node->isNewFile) c = 'N';
sprintf(&buf[len], " %ld %ld %ld %ld %c ",
node->sizebytes, node->sizelines,
node->filedate, node->filetime, c);
strcat(buf, node->node.ln_Name);
strcat(buf, " ");
if (node->filenote)
strcat(buf, node->filenote);
if (sizeof(buf) <= strlen(buf)) exit(250);
if (node->contents) {
argbuf = mallocFAM(strlen(node->contents)+strlen(buf)+5);
if (argbuf) {
strcpy(argbuf, buf);
strcat(argbuf, "\n");
strcat(argbuf, node->contents);
replyRexxCmd(msg, 0L, 0L, argbuf);
CMDuserreplied = 1;
freeFAM(argbuf);
}
else {
replyRexxCmd(msg, 0L, 0L, buf);
CMDuserreplied = 1;
}
}
else {
replyRexxCmd(msg, 0L, 0L, buf);
CMDuserreplied = 1;
}
}
/*
* This handler reconstructs the information for one file.
*/
void FAMrescan1(struct RexxMsg *msg, char *p)
{
struct ScanListNode * node;
struct FileInfoBlock * fib = malloc(sizeof(struct FileInfoBlock));
BPTR lock;
char buf[20];
while (*p <= ' ' && *p) p++;
if (*p == 0 || fib == NULL) {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
lock = Lock(p, SHARED_LOCK);
if (!lock) {
free(fib);
node = (struct ScanListNode *) FindName(&ScanList, p);
if (node) {
Remove((struct Node *) node);
FreeNode(node);
}
sprintf(buf, "%ld", IoErr());
replyRexxCmd(msg, 0L, 0L, buf);
CMDuserreplied = 1;
return;
}
if (!Examine(lock, fib)) {
sprintf(buf, "%ld", IoErr());
replyRexxCmd(msg, 0L, 0L, buf);
CMDuserreplied = 1;
free(fib);
UnLock(lock);
return;
}
UnLock(lock);
node = (struct ScanListNode *) FindName(&ScanList, p);
if (node) {
Remove((struct Node *) node);
}
node = FillInNode(fib, node);
if (node == NULL) {
free(fib);
sprintf(buf, "%ld", IoErr());
replyRexxCmd(msg, 0L, 0L, buf);
CMDuserreplied = 1;
return;
}
InsAlpha(&ScanList, node);
sprintf(buf, "%ld", IoErr());
replyRexxCmd(msg, 0L, 0L, buf);
CMDuserreplied = 1;
}
/*
* This handler reconstructs the information for all files.
*/
void FAMrescan(struct RexxMsg *msg, char *p)
{
/* right now, the argument is ignored */
struct ScanListNode * node, * next;
struct FileInfoBlock * fib = malloc(sizeof(struct FileInfoBlock));
BPTR lock;
#ifdef DEBUG
long c1 = 0, c2 = 0, c3 = 0; /* performance measurement */
#endif
while (*p <= ' ' && *p) p++;
if (fib == NULL) {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
lock = Lock("", SHARED_LOCK); /* always works on current dir for now */
if (!lock) {
free(fib);
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
if (!Examine(lock, fib)) {
free(fib);
UnLock(lock);
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
/* delete all files except NEWFILE files */
for (node = FirstNode(&ScanList); NotLastNode(node); node = NextNode(node)) {
if (!node->isNewFile)
node->isDeleted = 1; /* set it to deleted */
}
while (ExNext(lock, fib)) {
node = (struct ScanListNode *) FindName(&ScanList, fib->fib_FileName);
if (node != NULL && !node->isNewFile)
Remove((struct Node *) node);
if (node == NULL || !node->isNewFile)
node = FillInNode(fib, node);
if (node == NULL) {
free(fib);
UnLock(lock);
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
else {
/* see if we know where to put it already */
if ( !IsEmpty(ScanList) &&
StrLT(ScanList.lh_TailPred->ln_Name,
node->node.ln_Name) ) {
AddTail(&ScanList, (struct Node *) node);
InsAlphaPrev = node;
#ifdef DEBUG
c1 += 1;
#endif
}
else if(node->node.ln_Pred && /* has pred *
node->node.ln_Succ && /* has succ */
node->node.ln_Pred->ln_Pred && /* not at start */
node->node.ln_Succ->ln_Succ && /* not at end */
StrLT(node->node.ln_Pred->ln_Name, /* after pred */
node->node.ln_Name) &&
StrLT(node->node.ln_Name, /* before succ */
node->node.ln_Succ->ln_Name) ) {
InsertQ((struct Node *) node);
InsAlphaPrev = node;
#ifdef DEBUG
c2 += 1;
#endif
}
else {
InsAlpha(&ScanList, node);
InsAlphaPrev = node;
#ifdef DEBUG
c3 += 1;
#endif
}
}
}
UnLock(lock); /* make FileSystem designers happy */
InsAlphaPrev = NULL;
for (node = FirstNode(&ScanList); NotLastNode(node); ) {
assert(node && node->node.ln_Type == NT_FILENAME);
if (node->isDeleted) {
/* We never saw this node during the rescan! */
next = NextNode(node);
if (next != NULL) {
Remove((struct Node *) node);
FreeNode(node);
}
node = next;
}
else {
node = NextNode(node);
}
}
}
/*
* This handler matches general user information.
*/
void FAMgrep(struct RexxMsg *msg, char *p)
{
struct ScanListNode * node;
long count = 0;
char * buf;
char pad[2] = " ";
if (' ' < *p && *p <= 0x7F)
pad[0] = *p++;
while (*p <= ' ' && *p) p++;
if (GrepEntry == NULL) {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
for (node = FirstNode(&ScanList); NotLastNode(node); node = NextNode(node)) {
node->isGrepped = 0;
if ((*(GrepFunc *)(GrepEntry<<2L))(node, p)) {
node->isGrepped = 1;
count += strlen(node->node.ln_Name) + 1;
}
}
if (count == 0) {
replyRexxCmd(msg, 0L, 0L, "");
CMDuserreplied = 1;
return;
}
buf = mallocFAM(count + 4);
if (buf == NULL) {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
buf[0] = '\0';
for (node = FirstNode(&ScanList); NotLastNode(node); node = NextNode(node)) {
if (node->isGrepped) {
strcat(buf, pad);
strcat(buf, node->node.ln_Name);
node->isGrepped = 0;
}
}
replyRexxCmd(msg, 0L, 0L, &buf[1]); /* skip initial pad */
CMDuserreplied = 1;
freeFAM(buf);
}
/*
* This handler generates a new pseudo-file.
*/
void FAMnewfile(struct RexxMsg *msg, char *p)
{
struct ScanListNode * node;
long num, len;
short full;
short base;
char * basepnt;
char * name, * cp;
char buf[80];
extern long __stdargs CVa2i(char *, long *); /* in RexxGlue.a */
extern void __stdargs CVi2az(char *, long, long); /* in RexxGlue.a */
if (p[0] == '3' && p[1] == '6') {
base = 36;
p += 2;
basepnt = "0123456789ABCDEFGHIJLMNOPQRSTUVWXYZ";
}
else {
/* assume base 10 */
if (p[0] == '1' && p[1] == '0') p += 2;
basepnt = "0123456789";
base = 10;
}
while (*p <= ' ' && *p) p++;
if (IsEmpty(ScanList) || !*p) {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
for (node = FirstNode(&ScanList); NotLastNode(node); node = NextNode(node)) {
if (0 == strncmp(p, node->node.ln_Name, strlen(p))) {
break;
}
}
if (node == NULL) {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
for ( ; NotLastNode(node); node = NextNode(node)) {
if (0 != strncmp(p, node->node.ln_Name, strlen(p))) {
break;
}
}
node = (struct ScanListNode *) node->node.ln_Pred;
/* at this point, 'node' points to the node our's should follow */
name = node->node.ln_Name;
if (0 != strncmp(p, name, strlen(p))) {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
node = AllocNode();
if (0 == openRexxLib() || node == NULL) {
if (node) FreeNode(node);
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
for ( cp = name + strlen(p), len = 0, num = 0, full = 1;
isdigit(*cp);
cp++, len++) {
full = full && *cp == '9';
num = num * 10 + *cp - '0';
}
if (full) {
FreeNode(node);
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
strcpy(buf, p);
CVi2az(&buf[strlen(p)], num + 1, len);
node->node.ln_Name = strdupFAM(buf);
if (node->node.ln_Name == NULL) {
FreeNode(node);
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
node->isNewFile = 1; /* it's a NEWFILE file */
InsAlpha(&ScanList, node);
replyRexxCmd(msg, 0L, 0L, node->node.ln_Name);
CMDuserreplied = 1;
}
/*
* This handler locks or unlocks a file.
*/
void FAMlockfile(struct RexxMsg *msg, char *p)
{
struct ScanListNode * node;
char old = 0, new = 0;
int good = 1;
while (*p <= ' ' && *p) p++;
if (*p) old = *p++;
while (*p <= ' ' && *p) p++;
if (*p) new = *p++;
while (*p <= ' ' && *p) p++;
if (old != '-' && old != 'L') old = 0;
if (new != '-' && new != 'L') new = 0;
if (IsEmpty(ScanList) || !*p || old == 0 || new == 0) {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
for (node = FirstNode(&ScanList); NotLastNode(node); node = NextNode(node)) {
if (0 == strcmp(p, node->node.ln_Name)) {
break;
}
}
if (node == NULL) {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
good = good && (node->isNewFile == 0); /* can't lock NewFiles */
good = good && ((old == 'L') == node->isLocked);
if (good) {
node->isLocked = (new == 'L');
}
else {
replyRexxCmd(msg, ERRLEVEL, 0L, NULL);
CMDuserreplied = 1;
return;
}
}